<!DOCTYPE html>
<html class="has-navbar-fixed-top">
<head>
    <meta charset="utf-8">
<title>泛型 - wanzixin</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/outdated-browser/1.1.5/outdatedbrowser.min.css">


<link href="/Study/Java/zh-cn/%E6%B3%9B%E5%9E%8B/" rel="alternate" hreflang="en" />
    


<meta name="description" content="">





    <meta name="description" content="泛型是一种代码模板，可以用一套代码套用各种类型。本节我们详细讨论Java的泛型编程。">
<meta property="og:type" content="article">
<meta property="og:title" content="泛型">
<meta property="og:url" content="https://wanzixin.github.io/Study/Java/zh-cn/%E6%B3%9B%E5%9E%8B/index.html">
<meta property="og:site_name" content="wanzixin">
<meta property="og:description" content="泛型是一种代码模板，可以用一套代码套用各种类型。本节我们详细讨论Java的泛型编程。">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2021-05-01T01:29:07.000Z">
<meta property="article:modified_time" content="2021-05-30T13:33:41.775Z">
<meta property="article:author" content="wanzixin">
<meta name="twitter:card" content="summary">





<link rel="icon" href="/favicon.png">


<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Ovo|Source+Code+Pro">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">


<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/lightgallery/1.6.8/css/lightgallery.min.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/justifiedGallery/3.6.5/css/justifiedGallery.min.css">


<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/atom-one-light.min.css">


<link rel="stylesheet" href="/css/style.css">


<script defer src="//use.fontawesome.com/releases/v5.0.8/js/all.js"></script>


    
    
    
    
    
    
    
    
    
    

    


<meta name="generator" content="Hexo 5.4.0"></head>
<body>
    
<nav class="navbar is-transparent is-fixed-top navbar-main" role="navigation" aria-label="main navigation">
    <div class="container">
        <div class="navbar-brand">
            <a class="navbar-item navbar-logo" href="/zh-cn">
                
                    
                    wanzixin
                    
                
            </a>
            <div class="navbar-burger">
                <span></span>
                <span></span>
                <span></span>
            </div>
        </div>
        
        <div class="navbar-menu navbar-start">
            
            <a class="navbar-item "
               href="/archives">Archives</a>
            
            <a class="navbar-item "
               href="/categories">Categories</a>
            
            <a class="navbar-item "
               href="/categories/Diary">Diary</a>
            
            <a class="navbar-item "
               href="/categories/Gallery">Gallery</a>
            
            <a class="navbar-item "
               href="/categories/Study">Study</a>
            
            <a class="navbar-item "
               href="/categories/Item">Item</a>
            
            <a class="navbar-item "
               href="/about">About</a>
            
        </div>
        
        <div class="navbar-menu navbar-end">
            
            <a class="navbar-item search" title="搜索" href="javascript:;">
                <i class="fas fa-search"></i>
            </a>
            
            
            <div class="navbar-item is-hoverable has-dropdown is-hidden-mobile is-hidden-tablet-only toc">
                <a class="navbar-item" title="目录">
                    <i class="fa fa-list"></i>
                </a>
                <div class="navbar-dropdown is-right">
                    
                    
                    
                    
                    <a class="navbar-item" href="#什么是泛型">1&nbsp;&nbsp;<b>什么是泛型</b></a>
                    
                    
                    
                    <a class="navbar-item" href="#向上转型">1.1&nbsp;&nbsp;向上转型</a>
                    
                    
                    <hr class="navbar-divider">
                    
                    
                    <a class="navbar-item" href="#使用泛型">2&nbsp;&nbsp;<b>使用泛型</b></a>
                    
                    
                    
                    <a class="navbar-item" href="#泛型接口">2.1&nbsp;&nbsp;泛型接口</a>
                    
                    
                    <hr class="navbar-divider">
                    
                    
                    <a class="navbar-item" href="#编写泛型">3&nbsp;&nbsp;<b>编写泛型</b></a>
                    
                    
                    
                    <a class="navbar-item" href="#静态方法">3.1&nbsp;&nbsp;静态方法</a>
                    
                    
                    
                    <a class="navbar-item" href="#多个泛型类型">3.2&nbsp;&nbsp;多个泛型类型</a>
                    
                    
                    <hr class="navbar-divider">
                    
                    
                    <a class="navbar-item" href="#擦拭法">4&nbsp;&nbsp;<b>擦拭法</b></a>
                    
                    
                    
                    <a class="navbar-item" href="#不恰当的覆写方法">4.1&nbsp;&nbsp;不恰当的覆写方法</a>
                    
                    
                    
                    <a class="navbar-item" href="#泛型继承">4.2&nbsp;&nbsp;泛型继承</a>
                    
                    
                    
                    <a class="navbar-item" href="#小结">4.3&nbsp;&nbsp;小结</a>
                    
                    
                    <hr class="navbar-divider">
                    
                    
                    <a class="navbar-item" href="#extends通配符">5&nbsp;&nbsp;<b>extends通配符</b></a>
                    
                    
                    
                    <a class="navbar-item" href="#extends通配符的作用">5.1&nbsp;&nbsp;extends通配符的作用</a>
                    
                    
                    
                    <a class="navbar-item" href="#使用extends限定T类型">5.2&nbsp;&nbsp;使用extends限定T类型</a>
                    
                    
                    
                    <a class="navbar-item" href="#小结-1">5.3&nbsp;&nbsp;小结</a>
                    
                    
                    <hr class="navbar-divider">
                    
                    
                    <a class="navbar-item" href="#super通配符">6&nbsp;&nbsp;<b>super通配符</b></a>
                    
                    
                    
                    <a class="navbar-item" href="#对比extends和super通配符">6.1&nbsp;&nbsp;对比extends和super通配符</a>
                    
                    
                    
                    <a class="navbar-item" href="#PECS原则">6.2&nbsp;&nbsp;PECS原则</a>
                    
                    
                    
                    <a class="navbar-item" href="#无限定通配符">6.3&nbsp;&nbsp;无限定通配符</a>
                    
                    
                    <hr class="navbar-divider">
                    
                    
                    <a class="navbar-item" href="#泛型和反射">7&nbsp;&nbsp;<b>泛型和反射</b></a>
                    
                    
                    
                    <a class="navbar-item" href="#谨慎使用泛型可变参数">7.1&nbsp;&nbsp;谨慎使用泛型可变参数</a>
                    
                </div>
            </div>
            
            
            <a class="navbar-item" title="GitHub" target="_blank" rel="noopener" href="https://github.com/wanzixin">
                
                <i class="fab fa-github"></i>
                
            </a>
               
            
        </div>
    </div>
</nav>

    <section class="section">
    <div class="container">
    <article class="article content gallery" itemscope itemprop="blogPost">
    <h1 class="article-title is-size-3 is-size-4-mobile" itemprop="name">
        
            泛型
        
    </h1>
    <div class="article-meta columns is-variable is-1 is-multiline is-mobile is-size-7-mobile">
        <span class="column is-narrow">
            
                <span>5月 1 2021</span>
            
        </span>
        
        <span class="column is-narrow article-category">
            <i class="far fa-folder"></i>
            <a class="article-category-link" href="/zh-cn/categories/Study/">Study</a><span>></span><a class="article-category-link" href="/zh-cn/categories/Study/Java/">Java</a>
        </span>
        
        
        <span class="column is-narrow">
            
            
            36 分钟 读完 (约 5346 字)
        </span>
        
    </div>
    <div class="article-entry is-size-6-mobile" itemprop="articleBody">
    
        <html><head></head><body><p>泛型是一种代码模板，可以用一套代码套用各种类型。本节我们详细讨论Java的泛型编程。<span id="more"></span></p>
<h2 id="什么是泛型"><a href="#什么是泛型" class="headerlink" title="什么是泛型"></a>什么是泛型</h2><p>在介绍什么是泛型之前，我们先观察Java标准库提供的<code>ArrayList</code>，它可以看作“可变长度”的数组，因为用起来比数组更方便。</p>
<p>实际上ArrayList内部就是一个<code>Object[]</code>数组，配合存储一个当前分配的长度，就可以充当可变长度的数组。但如果用上述ArrayList存储String类型，会有两个缺点：</p>
<ul>
<li>需要强制转型</li>
<li>不方便，易出错</li>
</ul>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">ArrayList list = <span class="hljs-keyword">new</span> ArrayList();<br>list.add(<span class="hljs-string">"Hello"</span>);<br><span class="hljs-comment">// 获取到Object，必须强制转型为String:</span><br>String first = (String) list.get(<span class="hljs-number">0</span>);<br></code></pre></td></tr></tbody></table></figure>

<p>因为容易误转型，所以很容易出现ClassCastException。要解决上述问题，我们可以为String单独编写一种ArrayList：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StringArrayList</span> </span>{<br>    <span class="hljs-keyword">private</span> String[] array;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> size;<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">add</span><span class="hljs-params">(String e)</span> </span>{...}<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">remove</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{...}<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">get</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{...}<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>这样一来，存入的必须是String，取出的也一定是String，不需要强制转型，因为编译器会强制检查放入的类型。暂时解决了上述问题。</p>
<p>但新的问题又出现了，如果要存储Integer，还需要为Integer单独编写一种ArrayList，实际上，还需要为其他所有class单独编写一种ArrayList。这是不可能的，JDK的class就有上千个。</p>
<p>为了解决新的问题，我们必须把ArrayList变成一种模板：<code>ArrayList&lt;T&gt;</code>，代码如下：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ArrayList</span>&lt;<span class="hljs-title">T</span>&gt; </span>{<br>    <span class="hljs-keyword">private</span> T[] array;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> size;<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">add</span><span class="hljs-params">(T e)</span> </span>{...}<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">remove</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{...}<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> T <span class="hljs-title">get</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{...}<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>T可以是任何class。这样一来，我们就实现了，编写一次模板，可以创建任意类型的ArrayList。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 创建可以存储String的ArrayList:</span><br>ArrayList&lt;String&gt; strList = <span class="hljs-keyword">new</span> ArrayList&lt;String&gt;();<br><span class="hljs-comment">// 创建可以存储Float的ArrayList:</span><br>ArrayList&lt;Float&gt; floatList = <span class="hljs-keyword">new</span> ArrayList&lt;Float&gt;();<br><span class="hljs-comment">// 创建可以存储Person的ArrayList:</span><br>ArrayList&lt;Person&gt; personList = <span class="hljs-keyword">new</span> ArrayList&lt;Person&gt;();<br></code></pre></td></tr></tbody></table></figure>

<p>因此，泛型就是定义一种模板，然后在代码中为用到的类创建对应的<code>ArrayList&lt;类型&gt;</code>，由编译器针对类型做检查。这样一来，即实现了编写一次，万能匹配，又通过编译器保证了类型安全。</p>
<h3 id="向上转型"><a href="#向上转型" class="headerlink" title="向上转型"></a>向上转型</h3><p>在Java标准库中的<code>ArrayList&lt;T&gt;</code>实现了<code>List&lt;T&gt;</code>接口，它可以向上转型为<code>List&lt;T&gt;</code>。要特别注意，不能把<code>ArrayList&lt;Integer&gt;</code>向上转型为<code>ArrayList&lt;Number&gt;</code>或<code>List&lt;Number&gt;</code>。</p>
<p>我们把一个<code>ArrayList&lt;Integer&gt;</code>转型为<code>ArrayList&lt;Number&gt;</code>类型后，这个<code>ArrayList&lt;Number&gt;</code>就可以接受Float类型，因为Float是Number的子类。但是，<code>ArrayList&lt;Number&gt;</code>实际上和<code>ArrayList&lt;Integer&gt;</code>是同一个对象，也就是<code>ArrayList&lt;Integer&gt;</code>类型，它不可能接受Float类型， 所以在获取Integer的时候将产生ClassCastException。</p>
<p>实际上，编译器为了避免这种错误，根本就不允许把<code>ArrayList&lt;Integer&gt;</code>转型为<code>ArrayList&lt;Number&gt;</code>。</p>
<p>注意泛型的继承关系：可以把<code>ArrayList&lt;Integer&gt;</code>向上转型为<code>List&lt;Integer&gt;</code>（T不能变！），但不能把<code>ArrayList&lt;Integer&gt;</code>向上转型为<code>ArrayList&lt;Number&gt;</code>（T不能变成父类）。</p>
<h2 id="使用泛型"><a href="#使用泛型" class="headerlink" title="使用泛型"></a>使用泛型</h2><p>使用ArrayList时，如果不定义泛型类型，泛型类型实际上就是Object。此时，只能把<code>&lt;T&gt;</code>当Object使用，没有发挥泛型的优势。</p>
<p>当我们定义泛型类型<code>&lt;String&gt;</code>后，<code>List&lt;T&gt;</code>的泛型接口变为强类型<code>List&lt;String&gt;</code>。当我们定义泛型类型<code>&lt;Number&gt;</code>后，<code>List&lt;T&gt;</code>的泛型接口变为强类型<code>List&lt;Number&gt;</code>。编译器如果能自动推断出泛型类型，就可以省略后面的泛型类型。编译器看到泛型类型<code>List&lt;Number&gt;</code>就可以自动推断出后面的<code>ArrayList&lt;T&gt;</code>的泛型类型必须是<code>ArrayList&lt;Number&gt;</code>。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">List&lt;String&gt; strlist = <span class="hljs-keyword">new</span> ArrayList&lt;String&gt;();<br>List&lt;Number&gt; numlist = <span class="hljs-keyword">new</span> ArrayList&lt;Number&gt;();<br><span class="hljs-comment">// 可以省略后面的Number，编译器可以自动推断泛型类型：</span><br>List&lt;Number&gt; numlist2 = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();<br></code></pre></td></tr></tbody></table></figure>

<h3 id="泛型接口"><a href="#泛型接口" class="headerlink" title="泛型接口"></a>泛型接口</h3><p>还可以在接口中使用泛型。例如，<code>Arrays.sort(Object[])</code>可以对任意数组进行排序，但待排序的元素必须实现<code>Comparable&lt;T&gt;</code>这个泛型接口。可以直接对String数组进行排序，因为String本身已经实现了<code>Comparable&lt;String&gt;</code>接口。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Comparable</span>&lt;<span class="hljs-title">T</span>&gt; </span>{<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 返回负数: 当前实例比参数o小</span><br><span class="hljs-comment">     * 返回0: 当前实例与参数o相等</span><br><span class="hljs-comment">     * 返回正数: 当前实例比参数o大</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">compareTo</span><span class="hljs-params">(T o)</span></span>;<br>}<br></code></pre></td></tr></tbody></table></figure>

<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// sort</span><br><span class="hljs-keyword">import</span> java.util.Arrays;<br><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> </span>{<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{<br>        Person[] ps = <span class="hljs-keyword">new</span> Person[] {<br>            <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"Bob"</span>, <span class="hljs-number">61</span>),<br>            <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"Alice"</span>, <span class="hljs-number">88</span>),<br>            <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"Lily"</span>, <span class="hljs-number">75</span>),<br>        };<br>        Arrays.sort(ps);<br>        System.out.println(Arrays.toString(ps));<br>    }<br>}<br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Comparable</span>&lt;<span class="hljs-title">Person</span>&gt; </span>{<br>    String name;<br>    <span class="hljs-keyword">int</span> score;<br>    Person(String name, <span class="hljs-keyword">int</span> score) {<br>        <span class="hljs-keyword">this</span>.name = name;<br>        <span class="hljs-keyword">this</span>.score = score;<br>    }<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">compareTo</span><span class="hljs-params">(Person other)</span> </span>{<span class="hljs-comment">//也可以修改逻辑，按score从高到低排序</span><br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.name.compareTo(other.name);<br>    }<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.name + <span class="hljs-string">","</span> + <span class="hljs-keyword">this</span>.score;<br>    }<br>}<br></code></pre></td></tr></tbody></table></figure>

<h2 id="编写泛型"><a href="#编写泛型" class="headerlink" title="编写泛型"></a>编写泛型</h2><p>编写泛型类比普通类要复杂。通常来说，泛型类一般用在集合类，例如<code>ArrayList&lt;T&gt;</code>，我们很少需要编写泛型类。如果我们确实需要编写一个泛型类，那么应该怎么编写呢？</p>
<p>首先，按照某种类型，例如String，来编写类。然后标记所有的特定类型，这里是String。最后把特定类型String替换为T，并声明<code>&lt;T&gt;</code>。熟练之后可以直接从T开始编写。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pair</span> </span>{<br>    <span class="hljs-keyword">private</span> String first;<br>    <span class="hljs-keyword">private</span> String last;<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Pair</span><span class="hljs-params">(String first, String last)</span> </span>{<br>        <span class="hljs-keyword">this</span>.first = first;<br>        <span class="hljs-keyword">this</span>.last = last;<br>    }<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getFirst</span><span class="hljs-params">()</span> </span>{<br>        <span class="hljs-keyword">return</span> first;<br>    }<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getLast</span><span class="hljs-params">()</span> </span>{<br>        <span class="hljs-keyword">return</span> last;<br>    }<br>}<br><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pair</span>&lt;<span class="hljs-title">T</span>&gt; </span>{<br>    <span class="hljs-keyword">private</span> T first;<br>    <span class="hljs-keyword">private</span> T last;<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Pair</span><span class="hljs-params">(T first, T last)</span> </span>{<br>        <span class="hljs-keyword">this</span>.first = first;<br>        <span class="hljs-keyword">this</span>.last = last;<br>    }<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> T <span class="hljs-title">getFirst</span><span class="hljs-params">()</span> </span>{<br>        <span class="hljs-keyword">return</span> first;<br>    }<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> T <span class="hljs-title">getLast</span><span class="hljs-params">()</span> </span>{<br>        <span class="hljs-keyword">return</span> last;<br>    }<br>}<br></code></pre></td></tr></tbody></table></figure>

<h3 id="静态方法"><a href="#静态方法" class="headerlink" title="静态方法"></a>静态方法</h3><p>编写泛型类时，要特别注意，泛型类型<code>&lt;T&gt;</code>不能用于静态方法。例如：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pair</span>&lt;<span class="hljs-title">T</span>&gt; </span>{<br>    <span class="hljs-keyword">private</span> T first;<br>    <span class="hljs-keyword">private</span> T last;<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Pair</span><span class="hljs-params">(T first, T last)</span> </span>{<br>        <span class="hljs-keyword">this</span>.first = first;<br>        <span class="hljs-keyword">this</span>.last = last;<br>    }<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> T <span class="hljs-title">getFirst</span><span class="hljs-params">()</span> </span>{ ... }<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> T <span class="hljs-title">getLast</span><span class="hljs-params">()</span> </span>{ ... }<br><br>    <span class="hljs-comment">// 对静态方法使用&lt;T&gt;:</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Pair&lt;T&gt; <span class="hljs-title">create</span><span class="hljs-params">(T first, T last)</span> </span>{<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Pair&lt;T&gt;(first, last);<br>    }<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>上述代码会导致编译错误，我们无法在静态方法creat()的方法参数和返回类型上使用泛型类型T。对于静态方法，我们可以单独改写为泛型方法，只需要使用另一个类型就好了。这样才能清楚地将静态方法的泛型类型和实例类型的泛型类型区分开。</p>
<blockquote>
<p>普通的方法是通过类的实例来调用的，创建实例的过程调用了构造方法，也就是说对象已经知道这个时候类上面定义的<t>的具体类型了；而静态方法不需要对象实例来调用，所以也就不知道<t>的具体类型，虚拟机不允许这种情况发生，所以编译的时候就报错了。</t></t></p>
<p>或者说静态方法由于随着类的加载而加载，不能访问类的泛型（因为在创建对象的时候才确定），因此必须定义自己的泛型类型。</p>
</blockquote>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 静态泛型方法应该使用其他类型区分:</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> &lt;K&gt; <span class="hljs-function">Pair&lt;K&gt; <span class="hljs-title">create</span><span class="hljs-params">(K first, K last)</span> </span>{<br>	<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Pair&lt;K&gt;(first, last);<br>}<br></code></pre></td></tr></tbody></table></figure>

<h3 id="多个泛型类型"><a href="#多个泛型类型" class="headerlink" title="多个泛型类型"></a>多个泛型类型</h3><p>泛型还可以定义多种类型。例如，我们希望Pair不总是存储两个类型一样的对象，就可以使用类型<code>&lt;T, K&gt;</code>。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pair</span>&lt;<span class="hljs-title">T</span>, <span class="hljs-title">K</span>&gt; </span>{<br>    <span class="hljs-keyword">private</span> T first;<br>    <span class="hljs-keyword">private</span> K last;<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Pair</span><span class="hljs-params">(T first, K last)</span> </span>{<br>        <span class="hljs-keyword">this</span>.first = first;<br>        <span class="hljs-keyword">this</span>.last = last;<br>    }<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> T <span class="hljs-title">getFirst</span><span class="hljs-params">()</span> </span>{ ... }<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> K <span class="hljs-title">getLast</span><span class="hljs-params">()</span> </span>{ ... }<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>使用的时候，需要指出两种类型：<code>Pair&lt;String, Integer&gt; p = new Pair&lt;&gt;("test", 123);</code>。</p>
<p>Java标准库的<code>Map&lt;K, V&gt;</code>就是使用两种泛型类型的例子。它对Key使用一种类型，对Value使用另一种类型。</p>
<h2 id="擦拭法"><a href="#擦拭法" class="headerlink" title="擦拭法"></a>擦拭法</h2><p>泛型是一种类似“模板代码”的技术，不同语言的泛型实现方式不一定相同。Java语言的泛型实现方法是擦拭法（Type Erasure）。所谓擦拭法，是指虚拟机对泛型其实一无所知，所有的工作都是编译器做的。</p>
<ul>
<li>编译器把类型<code>&lt;T&gt;</code>视为Object</li>
<li>编译器根据<code>&lt;T&gt;</code>实现安全的强制转型</li>
</ul>
<p>所以，Java的泛型是由编译器在编译时实行的，编译器内部永远把所有类型<code>&lt;T&gt;</code>视为Object处理，但是，在需要转型的时候，编译器会根据T的类型自动为我们实现安全地强制转型。了解了Java泛型的实现方法，我们就知道了Java泛型的局限。</p>
<ul>
<li>局限一：<code>&lt;T&gt;</code>不能是基本类型，因为实际类型是Object，Object类型无法持有基本类型。</li>
<li>局限二：无法取得带泛型的Class，换句话说，所有泛型实例无论T的类型是什么，getClass()返回同一个Class实例，因为它们编译后全都是<code>Pair&lt;Object&gt;</code>。</li>
<li>局限三：无法判断带泛型的类型。原因同局限二。</li>
</ul>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">Pair&lt;Integer&gt; p = <span class="hljs-keyword">new</span> Pair&lt;&gt;(<span class="hljs-number">123</span>, <span class="hljs-number">456</span>);<br><span class="hljs-comment">// Compile error:</span><br><span class="hljs-keyword">if</span> (p <span class="hljs-keyword">instanceof</span> Pair&lt;String&gt;) {<br>}<br></code></pre></td></tr></tbody></table></figure>

<ul>
<li>局限四：不能实例化T类型。</li>
</ul>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pair</span>&lt;<span class="hljs-title">T</span>&gt; </span>{<br>    <span class="hljs-keyword">private</span> T first;<br>    <span class="hljs-keyword">private</span> T last;<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Pair</span><span class="hljs-params">()</span> </span>{<br>        <span class="hljs-comment">// Compile error:</span><br>        <span class="hljs-comment">// 因为擦拭后，下面两句分别变为first = new Object();last = new Object();</span><br>        <span class="hljs-comment">// 这样一来，创建new Pair&lt;String&gt;()和new Pair&lt;Integer&gt;()就全部变成了Object</span><br>        <span class="hljs-comment">// 编译器会阻止这种类型不对的代码</span><br>        first = <span class="hljs-keyword">new</span> T();<br>        last = <span class="hljs-keyword">new</span> T();<br>    }<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>要实例化T类型，我们必须借助额外的<code>Class&lt;T&gt;</code>参数，借助<code>Class&lt;T&gt;</code>参数并通过反射来实例化T类型，使用的时候，也必须传入<code>Class&lt;T&gt;</code>。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pair</span>&lt;<span class="hljs-title">T</span>&gt; </span>{<br>    <span class="hljs-keyword">private</span> T first;<br>    <span class="hljs-keyword">private</span> T last;<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Pair</span><span class="hljs-params">(Class&lt;T&gt; clazz)</span> </span>{<br>        first = clazz.newInstance();<br>        last = clazz.newInstance();<br>    }<br>}<br><br>Pair&lt;String&gt; pair = <span class="hljs-keyword">new</span> Pair&lt;&gt;(String.class);<span class="hljs-comment">//因为传入了Class&lt;String&gt;的实例，所以我们借助String.class就可以实例化String类型。</span><br></code></pre></td></tr></tbody></table></figure>

<h3 id="不恰当的覆写方法"><a href="#不恰当的覆写方法" class="headerlink" title="不恰当的覆写方法"></a>不恰当的覆写方法</h3><p>有时候，一个看似正确定义的方法会无法通过编译，比如：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pair</span>&lt;<span class="hljs-title">T</span>&gt; </span>{<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">equals</span><span class="hljs-params">(T t)</span> </span>{<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span> == t;<br>    }<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>这是因为，定义的<code>equals(T t)</code>方法实际上会被擦拭成<code>equals(Object t)</code>，而这个方法是继承自Object的，编译器会阻止一个实际会变成覆写的泛型方法的定义。换个方法名，比如same，避开Object.equals(Object)的冲突就可以成功编译。</p>
<h3 id="泛型继承"><a href="#泛型继承" class="headerlink" title="泛型继承"></a>泛型继承</h3><p>一个类可以继承自一个泛型类。例如：父类的类型是<code>Pair&lt;Integer&gt;</code>，子类的类型是IntPair，可以这么继承：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IntPair</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Pair</span>&lt;<span class="hljs-title">Integer</span>&gt; </span>{<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>使用的时候，因为子类IntPair并没有泛型继承，所以正常使用即可。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">IntPair ip = <span class="hljs-keyword">new</span> IntPair(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);<br></code></pre></td></tr></tbody></table></figure>

<p>前面讲了，我们无法获取<code>Pair&lt;T&gt;</code>的T类型，即给定一个变量<code>Pair&lt;Integer&gt; p</code>，无法从p中获取到Integer类型。但是，在父类是泛型类型的情况下，编译器就必须把类型T保存到子类的class文件中，不然编译器就不知道IntPair只能存取Integer这种类型。</p>
<p>在继承了泛型类型的情况下，子类可以获取父类的泛型类型。代码较为复杂，这里就不贴了。</p>
<h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p>Java的泛型是采用擦拭法实现的；</p>
<p>擦拭法决定了泛型<code>&lt;T&gt;</code>：</p>
<ul>
<li>不能是基本类型，例如：<code>int</code>；</li>
<li>不能获取带泛型类型的<code>Class</code>，例如：<code>Pair&lt;String&gt;.class</code>；</li>
<li>不能判断带泛型类型的类型，例如：<code>x instanceof Pair&lt;String&gt;</code>；</li>
<li>不能实例化<code>T</code>类型，例如：<code>new T()</code>。</li>
</ul>
<p>泛型方法要防止重复定义方法，例如：<code>public boolean equals(T obj)</code>；</p>
<p>子类可以获取父类的泛型类型<code>&lt;T&gt;</code>。</p>
<h2 id="extends通配符"><a href="#extends通配符" class="headerlink" title="extends通配符"></a>extends通配符</h2><p>我们前面已经讲到泛型的继承关系：<code>Pair&lt;Integer&gt;</code>不是<code>Pair&lt;Number&gt;</code>的子类。假设我们定义了<code>Pair&lt;T&gt;</code>，然后又针对<code>Pair&lt;Number&gt;</code>类型写了一个静态方法，它接受的参数类型是<code>Pair&lt;Number&gt;</code>。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pair</span>&lt;<span class="hljs-title">T</span>&gt; </span>{ ... }<br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PairHelper</span> </span>{<br>    <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">add</span><span class="hljs-params">(Pair&lt;Number&gt; p)</span> </span>{<br>        Number first = p.getFirst();<br>        Number last = p.getLast();<br>        <span class="hljs-keyword">return</span> first.intValue() + last.intValue();<br>    }<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>上述代码是可以正常编译的，使用的时候，我们传入：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">int</span> sum = PairHelper.add(<span class="hljs-keyword">new</span> Pair&lt;Number&gt;(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>));<br></code></pre></td></tr></tbody></table></figure>

<p>注意，传入的类型是<code>Pair&lt;Number&gt;</code>，实际参数类型是<code>&lt;Integer, Integer&gt;</code>。既然实际参数是<code>Integer</code>类型，试试传入<code>Pair&lt;Integer&gt;</code>。</p>
<p>直接运行会得到一个编译错误，原因很明显，因为<code>Pair&lt;Integer&gt;</code>不是<code>Pair&lt;Number&gt;</code>的子类，因此<code>add(Pair&lt;Number&gt;)</code>不接受参数类型<code>Pair&lt;Integer&gt;</code>。但是从add()方法的代码可知，传入<code>Pair&lt;Integer&gt;</code>完全符合内部代码的类型规范，因为语句：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">Number first = p.getFirst();<br>Number last = p.getLast();<br></code></pre></td></tr></tbody></table></figure>

<p>实际类型是<code>Integer</code>，引用类型是<code>Number</code>，没有问题。问题在于方法参数类型定死了只能传入<code>Pair&lt;Number&gt;</code>。</p>
<p>有没有办法使得方法参数接受<code>Pair&lt;Integer&gt;</code>？办法是有的，这就是使用<code>Pair&lt;? extends Number&gt;</code>使得方法接受所有泛型类型为<code>Number</code>及其子类的Pair类型。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">add</span><span class="hljs-params">(Pair&lt;? extends Number&gt; p)</span> </span>{<br>    Number first = p.getFirst();<br>    Number last = p.getLast();<br>    <span class="hljs-keyword">return</span> first.intValue() + last.intValue();<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>这样一来，给方法传入<code>Pair&lt;Integer&gt;</code>类型时，它符合参数<code>Pair&lt;? extends Number&gt;</code>类型。这种使用<code>&lt;? extends Number&gt;</code>的泛型定义称之为上界通配符（Upper Bounds Wildcards），即把泛型类型T的上界限定在<code>Number</code>了。</p>
<p>除了可以传入<code>Pair&lt;Integer&gt;</code>类型，我们还可以传入<code>Pair&lt;Double&gt;</code>类型，<code>Pair&lt;BigDecimal&gt;</code>类型等等，因为<code>Double</code>和<code>BigDecimal</code>都是<code>Number</code>的子类。</p>
<p><code>&lt;? extends Number&gt;</code>通配符的一个重要限制：方法参数签名无法传递任何<code>Number</code>的子类型。这里唯一的例外是可以给方法参数传入<code>null</code>。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">add</span><span class="hljs-params">(Pair&lt;? extends Number&gt; p)</span> </span>{<br>    Number first = p.getFirst();<br>    Number last = p.getLast();<br>    p.setFirst(<span class="hljs-keyword">new</span> Integer(first.intValue() + <span class="hljs-number">100</span>));<span class="hljs-comment">// </span><br>    p.setLast(<span class="hljs-keyword">new</span> Integer(last.intValue() + <span class="hljs-number">100</span>));<span class="hljs-comment">//</span><br>    <span class="hljs-keyword">return</span> p.getFirst().intValue() + p.getFirst().intValue();<br>}<br></code></pre></td></tr></tbody></table></figure>

<h3 id="extends通配符的作用"><a href="#extends通配符的作用" class="headerlink" title="extends通配符的作用"></a>extends通配符的作用</h3><p>使用extends通配符只能读，不能写。</p>
<h3 id="使用extends限定T类型"><a href="#使用extends限定T类型" class="headerlink" title="使用extends限定T类型"></a>使用extends限定T类型</h3><p>在定义泛型类型<code>Pair&lt;T&gt;</code>的时候，也可以使用extends通配符来限定T的类型。因为Number、Integer和Double都符合<code>&lt;T extends Number&gt;</code>。而非Number类型将无法通过编译。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pair</span>&lt;<span class="hljs-title">T</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Number</span>&gt; </span>{ ... }<br>Pair&lt;Number&gt; p1 = <span class="hljs-keyword">null</span>;<br>Pair&lt;Integer&gt; p2 = <span class="hljs-keyword">new</span> Pair&lt;&gt;(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);<br>Pair&lt;Double&gt; p3 = <span class="hljs-keyword">null</span>;<br>Pair&lt;String&gt; p1 = <span class="hljs-keyword">null</span>; <span class="hljs-comment">// compile error!</span><br>Pair&lt;Object&gt; p2 = <span class="hljs-keyword">null</span>; <span class="hljs-comment">// compile error!</span><br></code></pre></td></tr></tbody></table></figure>

<h3 id="小结-1"><a href="#小结-1" class="headerlink" title="小结"></a>小结</h3><p>使用类似<code>&lt;? extends Number&gt;</code>通配符作为方法参数时表示：</p>
<ul>
<li>方法内部可以调用获取<code>Number</code>引用的方法，例如：<code>Number n = obj.getFirst();</code>；</li>
<li>方法内部无法调用传入<code>Number</code>引用的方法（<code>null</code>除外），例如：<code>obj.setFirst(Number n);</code>。</li>
</ul>
<p>即一句话总结：使用<code>extends</code>通配符表示可以读，不能写。</p>
<p>使用类似<code>&lt;T extends Number&gt;</code>定义泛型类时表示：</p>
<ul>
<li>泛型类型限定为<code>Number</code>以及<code>Number</code>的子类。</li>
</ul>
<h2 id="super通配符"><a href="#super通配符" class="headerlink" title="super通配符"></a>super通配符</h2><p>使用<code>&lt;? super Integer&gt;</code>通配符表示：</p>
<ul>
<li>允许调用<code>set(? super Integer)</code>方法传入Integer的引用；</li>
<li>不允许调用<code>get()</code>方法获得Integer的引用。</li>
</ul>
<p>唯一例外是可以获取Object的引用：<code>Object o = p.getFirst()</code>。</p>
<p>换句话说，使用<code>&lt;? super Integer&gt;</code>通配符作为方法参数，表示方法内部代码对于参数只能写，不能读。</p>
<h3 id="对比extends和super通配符"><a href="#对比extends和super通配符" class="headerlink" title="对比extends和super通配符"></a>对比extends和super通配符</h3><p>作为方法参数，<code>&lt;? extends T&gt;</code>类型和<code>&lt;? super T&gt;</code>类型的区别在于：</p>
<ul>
<li><code>&lt;? extends T&gt;</code>允许调用读方法<code>T get()</code>获取T的引用，但不允许调用写方法<code>set(T)</code>传入T的引用（传入null除外）</li>
<li><code>&lt;? super T&gt;</code>允许调用写方法<code>set(T)</code>传入T的引用，但不允许调用读方法<code>T get()</code>获取T的引用（获取Object除外）</li>
</ul>
<p>一个是允许读不允许写，一个是允许写不允许读。</p>
<p>先记住上面的结论，我们来看Java标准库的<code>Collections</code>类定义的<code>copy()</code>方法：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Collections</span> </span>{<br>    <span class="hljs-comment">// 把src的每个元素复制到dest中:</span><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> &lt;T&gt; <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">copy</span><span class="hljs-params">(List&lt;? <span class="hljs-keyword">super</span> T&gt; dest, List&lt;? extends T&gt; src)</span> </span>{<br>        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>; i&lt;src.size(); i++) {<br>            T t = src.get(i);<br>            dest.add(t);<br>        }<br>    }<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>它的作用是把每一个List的每个元素依次添加到另一个<code>List</code>。它的第一个参数是<code>List&lt;? super T&gt;</code>，表示目标List，第二个参数<code>List&lt;? extends T&gt;</code>，表示要复制的<code>List</code>。我们可以简单地用for循环实现复制。在for循环中，我们可以看到，对于类型<code>&lt;? extends T&gt;</code>的变量<code>src</code>，我们可以安全地获取类型T的引用，而对于类型<code>&lt;? super T&gt;</code>的变量dest，我们可以安全地传入T的引用。</p>
<p>这个<code>copy()</code>方法的定义就完美地展示了extends和super的意图：</p>
<ul>
<li><code>copy()</code>方法内部不会读取dest，因为不能调用<code>dest.get()</code>来获取T的引用；</li>
<li><code>copy()</code>方法内部也不会修改src，因为不能调用<code>src.add(T)</code>。</li>
</ul>
<p>这是由编译器检查来实现的。如果在方法代码中意外修改了src，或者意外读取了dest，就会导致一个编译错误。</p>
<p>这个copy()方法的另一个好处是可以安全地把一个<code>List&lt;Integer&gt;</code>添加到<code>List&lt;Number&gt;</code>，但是反过来无法添加。</p>
<p>这些都是通过super和extends通配符，并由编译器强制检查实现的。</p>
<h3 id="PECS原则"><a href="#PECS原则" class="headerlink" title="PECS原则"></a>PECS原则</h3><p>何时使用extends，何时使用super？为了便于记忆，我们可以用PECS原则：Producer Extends Consumer Super。</p>
<p>即：如果需要返回T，它是生产者（Producer），要使用extends通配符；如果需要写入T，它是消费者（Consumer），要使用super通配符。</p>
<h3 id="无限定通配符"><a href="#无限定通配符" class="headerlink" title="无限定通配符"></a>无限定通配符</h3><p>实际上，Java的泛型还允许使用无限定通配符（Unbounded Wildcard Type），即只定义一个?：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">sample</span><span class="hljs-params">(Pair&lt;?&gt; p)</span> </span>{<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>因为<code>&lt;?&gt;</code>通配符既没有extends，也没有super，因此：</p>
<ul>
<li>不允许调用<code>set(T)</code>方法并传入引用（null除外）；</li>
<li>不允许调用<code>T get()</code>方法并获取<code>T</code>引用（只能获取Object引用）。</li>
</ul>
<p>换句话说，既不能读，也不能写，那只能做一些null判断：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isNull</span><span class="hljs-params">(Pair&lt;?&gt; p)</span> </span>{<br>    <span class="hljs-keyword">return</span> p.getFirst() == <span class="hljs-keyword">null</span> || p.getLast() == <span class="hljs-keyword">null</span>;<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>大多数情况下，可以引入泛型参数<code>&lt;T&gt;</code>消除<code>&lt;?&gt;</code>通配符：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">static</span> &lt;T&gt; <span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">isNull</span><span class="hljs-params">(Pair&lt;T&gt; p)</span> </span>{<br>    <span class="hljs-keyword">return</span> p.getFirst() == <span class="hljs-keyword">null</span> || p.getLast() == <span class="hljs-keyword">null</span>;<br>}<br></code></pre></td></tr></tbody></table></figure>

<p><code>&lt;?&gt;</code>通配符有一个独特的特点，就是：<code>Pair&lt;?&gt;</code>是所有<code>Pair&lt;T&gt;</code>的超类。</p>
<h2 id="泛型和反射"><a href="#泛型和反射" class="headerlink" title="泛型和反射"></a>泛型和反射</h2><p>Java的部分反射API也是泛型。例如，<code>Class&lt;T&gt;</code>就是泛型。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// compile warning:</span><br>Class clazz = String.class;<br>String str = (String) clazz.newInstance();<br><br><span class="hljs-comment">// no warning:</span><br>Class&lt;String&gt; clazz = String.class;<br>String str = clazz.newInstance();<br></code></pre></td></tr></tbody></table></figure>

<p>调用Class的<code>getSuperclass()</code>方法返回的Class类型是<code>Class&lt;? super T&gt;</code>：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">Class&lt;? <span class="hljs-keyword">super</span> String&gt; sup = String.class.getSuperclass();<br></code></pre></td></tr></tbody></table></figure>

<p>构造方法<code>Constructor&lt;T&gt;</code>也是泛型：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java">Class&lt;Integer&gt; clazz = Integer.class;<br>Constructor&lt;Integer&gt; cons = clazz.getConstructor(<span class="hljs-keyword">int</span>.class);<br>Integer i = cons.newInstance(<span class="hljs-number">123</span>);<br></code></pre></td></tr></tbody></table></figure>

<p>我们可以声明带泛型的数组，但不能用new操作符创建带泛型的数组。必须通过强制转型实现带泛型的数组。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">Pair&lt;String&gt;[] ps = <span class="hljs-keyword">null</span>; <span class="hljs-comment">// ok</span><br>Pair&lt;String&gt;[] ps = <span class="hljs-keyword">new</span> Pair&lt;String&gt;[<span class="hljs-number">2</span>]; <span class="hljs-comment">// compile error!</span><br><br><span class="hljs-meta">@SuppressWarnings("unchecked")</span><br>Pair&lt;String&gt;[] ps = (Pair&lt;String&gt;[]) <span class="hljs-keyword">new</span> Pair[<span class="hljs-number">2</span>];<br></code></pre></td></tr></tbody></table></figure>

<p>使用泛型数组要特别小心，因为数组实际上在运行期没有泛型，编译器可以强制检查变量ps，因为它的类型是泛型数组。但是，编译器不会检查变量arr，因为它不是泛型数组。因为这两个变量实际上指向同一个数组，所以，操作arr可能导致从ps获取元素时报错，例如，以下代码演示了不安全地使用带泛型的数组：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java">Pair[] arr = <span class="hljs-keyword">new</span> Pair[<span class="hljs-number">2</span>];<br>Pair&lt;String&gt;[] ps = (Pair&lt;String&gt;[]) arr;<br><br>ps[<span class="hljs-number">0</span>] = <span class="hljs-keyword">new</span> Pair&lt;String&gt;(<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>);<br>arr[<span class="hljs-number">1</span>] = <span class="hljs-keyword">new</span> Pair&lt;Integer&gt;(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>);<br><br><span class="hljs-comment">// ClassCastException:</span><br>Pair&lt;String&gt; p = ps[<span class="hljs-number">1</span>];<br>String s = p.getFirst();<br></code></pre></td></tr></tbody></table></figure>

<p>要安全地使用泛型数组，必须扔掉arr的引用，由于拿不到原始数组的引用，就只能对泛型数组ps进行操作，这种操作就是安全的。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SuppressWarnings("unchecked")</span><br>Pair&lt;String&gt;[] ps = (Pair&lt;String&gt;[]) <span class="hljs-keyword">new</span> Pair[<span class="hljs-number">2</span>];<br></code></pre></td></tr></tbody></table></figure>

<p>带泛型的数组实际上是编译器的类型擦除，所以我们不能直接创建泛型数组T[]，因为擦拭后代码变成Object[]。必须借助<code>Class&lt;T&gt;</code>来创建泛型数组。</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// compile error:</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Abc</span>&lt;<span class="hljs-title">T</span>&gt; </span>{<br>    T[] createArray() {<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> T[<span class="hljs-number">5</span>];<br>    }<br>}<br><br>T[] createArray(Class&lt;T&gt; cls) {<br>    <span class="hljs-keyword">return</span> (T[]) Array.newInstance(cls, <span class="hljs-number">5</span>);<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>我们还可以利用可变参数创建泛型数组T[]：</p>
<figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ArrayHelper</span> </span>{<br>    <span class="hljs-meta">@SafeVarargs</span><br>    <span class="hljs-keyword">static</span> &lt;T&gt; T[] asArray(T... objs) {<br>        <span class="hljs-keyword">return</span> objs;<br>    }<br>}<br><br>String[] ss = ArrayHelper.asArray(<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"c"</span>);<br>Integer[] ns = ArrayHelper.asArray(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>);<br></code></pre></td></tr></tbody></table></figure>

<h3 id="谨慎使用泛型可变参数"><a href="#谨慎使用泛型可变参数" class="headerlink" title="谨慎使用泛型可变参数"></a>谨慎使用泛型可变参数</h3><figure class="highlight java hljs"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> </span>{<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{<br>        String[] arr = asArray(<span class="hljs-string">"one"</span>, <span class="hljs-string">"two"</span>, <span class="hljs-string">"three"</span>);<br>        System.out.println(Arrays.toString(arr));<br>        <span class="hljs-comment">// ClassCastException:</span><br>        String[] firstTwo = pickTwo(<span class="hljs-string">"one"</span>, <span class="hljs-string">"two"</span>, <span class="hljs-string">"three"</span>);<br>        System.out.println(Arrays.toString(firstTwo));<br>    }<br><br>    <span class="hljs-keyword">static</span> &lt;K&gt; K[] pickTwo(K k1, K k2, K k3) {<br>        <span class="hljs-keyword">return</span> asArray(k1, k2);<br>    }<br><br>    <span class="hljs-keyword">static</span> &lt;T&gt; T[] asArray(T... objs) {<br>        <span class="hljs-keyword">return</span> objs;<br>    }<br>}<br></code></pre></td></tr></tbody></table></figure>

<p>直接调用<code>asArray(T...)</code>似乎没有问题，但是在另一个方法中，我们返回一个泛型数组就会产生<code>ClassCastException</code>，原因还是因为擦拭法，在<code>pickTwo()</code>方法内部，编译器无法检测<code>K[]</code>的正确类型，因此返回了<code>Object[]</code>。如果仔细观察，可以发现编译器对所有可变泛型参数都会发出警告，除非确认完全没有问题，才可以用<code>@SafeVarargs</code>消除警告。如果在方法内部创建了泛型数组，最好不要将它返回给外部使用。</p>
</body></html>
    
    </div>
    
    
    <div class="columns is-mobile is-multiline article-nav">
        <span class="column is-12-mobile is-half-desktop  article-nav-prev">
            
            <a href="/Study/Java/zh-cn/%E9%9B%86%E5%90%88/">集合</a>
            
        </span>
        <span class="column is-12-mobile is-half-desktop  article-nav-next">
            
            <a href="/Study/Java/zh-cn/%E6%B3%A8%E8%A7%A3/">注解</a>
            
        </span>
    </div>
    
</article>


<div class="sharebox">
    
<div class="sharethis-inline-share-buttons"></div>
<script type='text/javascript' src='//platform-api.sharethis.com/js/sharethis.js#property=608c1408daac690012507aa2&amp;product=sop' async='async'></script>

</div>



    </div>
</section>
    <footer class="footer">
    <div class="container">
        <div class="columns content">
            <div class="column is-narrow has-text-centered">
                &copy; 2021 wanzixin&nbsp;
                Powered by <a href="http://hexo.io/" target="_blank">Hexo</a> & <a
                        target="_blank" rel="noopener" href="http://github.com/ppoffice/hexo-theme-minos">Minos</a>
            </div>
            <div class="column is-hidden-mobile"></div>

            
            <div class="column is-narrow">
                <div class="columns is-mobile is-multiline is-centered">
                
                    
                <a class="column is-narrow has-text-black" title="GitHub" target="_blank" rel="noopener" href="https://github.com/ppoffice/hexo-theme-minos">
                    
                    GitHub
                    
                </a>
                
                </div>
            </div>
            
            
<div class="column is-narrow has-text-centered">
    <div class="dropdown is-up is-right is-hoverable" style="margin-top: -0.2em;">
        <div class="dropdown-trigger">
            <button class="button is-small" aria-haspopup="true" aria-controls="dropdown-menu7">
                <span class="icon">
                    <i class="fas fa-globe"></i>
                </span>
                <span>简体中文</span>
                <span class="icon is-small">
            <i class="fas fa-angle-down" aria-hidden="true"></i>
          </span>
            </button>
        </div>
        <div class="dropdown-menu has-text-left" role="menu">
            <div class="dropdown-content">
            
                <a href="/Study/Java/zh-cn/%E6%B3%9B%E5%9E%8B/" class="dropdown-item">
                    English
                </a>
            
                <a href="/zh-cn/Study/Java/zh-cn/%E6%B3%9B%E5%9E%8B/" class="dropdown-item">
                    简体中文
                </a>
            
            </div>
        </div>
    </div>
</div>

        </div>
    </div>
</footer>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment-with-locales.min.js"></script>

<!-- test if the browser is outdated -->
<div id="outdated">
    <h6>Your browser is out-of-date!</h6>
    <p>Update your browser to view this website correctly. <a id="btnUpdateBrowser" target="_blank" rel="noopener" href="http://outdatedbrowser.com/">Update my browser now </a></p>
    <p class="last"><a href="#" id="btnCloseUpdateBrowser" title="Close">&times;</a></p>
</div>
<script src="//cdnjs.cloudflare.com/ajax/libs/outdated-browser/1.1.5/outdatedbrowser.min.js"></script>
<script>
    $(document).ready(function () {
        // plugin function, place inside DOM ready function
        outdatedBrowser({
            bgColor: '#f25648',
            color: '#ffffff',
            lowerThan: 'flex'
        })
    });
</script>

<script>
    window.FontAwesomeConfig = {
        searchPseudoElements: true
    }
    moment.locale("zh-CN");
</script>


    
    
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.4/MathJax.js?config=TeX-MML-AM_CHTML"></script>
<script>
    MathJax.Hub.Config({
        "HTML-CSS": {
            matchFontHeight: false
        },
        SVG: {
            matchFontHeight: false
        },
        CommonHTML: {
            matchFontHeight: false
        },
        tex2jax: {
            inlineMath: [
                ['$','$'],
                ['\\(','\\)']
            ]
        }
    });
</script>

    
    
    
    
<script src="//cdnjs.cloudflare.com/ajax/libs/lightgallery/1.6.8/js/lightgallery-all.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/justifiedGallery/3.6.5/js/jquery.justifiedGallery.min.js"></script>
<script>
    (function ($) {
        $(document).ready(function () {
            if (typeof($.fn.lightGallery) === 'function') {
                $('.article.gallery').lightGallery({ selector: '.gallery-item' });
            }
            if (typeof($.fn.justifiedGallery) === 'function') {
                $('.justified-gallery').justifiedGallery();
            }
        });
    })(jQuery);
</script>

    
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js"></script>
    <style>
        .hljs {
            position: relative;
        }

        .hljs .clipboard-btn {
            float: right;
            color: #9a9a9a;
            background: none;
            border: none;
            cursor: pointer;
        }

        .hljs .clipboard-btn:hover {
          color: #8a8a8a;
        }

        .hljs > .clipboard-btn {
            display: none;
            position: absolute;
            right: 4px;
            top: 4px;
        }

        .hljs:hover > .clipboard-btn {
            display: inline;
        }

        .hljs > figcaption > .clipboard-btn {
            margin-right: 4px;
        }
    </style>
    <script>
      $(document).ready(function () {
        $('figure.hljs').each(function(i, figure) {
          var codeId = 'code-' + i;
          var code = figure.querySelector('.code');
          var copyButton = $('<button>Copy <i class="far fa-clipboard"></i></button>');
          code.id = codeId;
          copyButton.addClass('clipboard-btn');
          copyButton.attr('data-clipboard-target-id', codeId);

          var figcaption = figure.querySelector('figcaption');

          if (figcaption) {
            figcaption.append(copyButton[0]);
          } else {
            figure.prepend(copyButton[0]);
          }
        })

        var clipboard = new ClipboardJS('.clipboard-btn', {
          target: function(trigger) {
            return document.getElementById(trigger.getAttribute('data-clipboard-target-id'));
          }
        });
        clipboard.on('success', function(e) {
          e.clearSelection();
        })
      })
    </script>

    
    

    



<script src="/js/script.js"></script>


    
    <div class="searchbox ins-search">
    <div class="searchbox-mask"></div>
    <div class="searchbox-container ins-search-container">
        <div class="searchbox-input-wrapper">
            <input type="text" class="searchbox-input ins-search-input" placeholder="站内搜索" />
            <span class="searchbox-close ins-close ins-selectable"><i class="fa fa-times-circle"></i></span>
        </div>
        <div class="searchbox-result-wrapper ins-section-wrapper">
            <div class="ins-section-container"></div>
        </div>
    </div>
</div>
<script>
    (function (window) {
        var INSIGHT_CONFIG = {
            TRANSLATION: {
                POSTS: '文章',
                PAGES: '页面',
                CATEGORIES: '分类',
                TAGS: '标签',
                UNTITLED: '(无标题)',
            },
            CONTENT_URL: '/content.zh-cn.json',
        };
        window.INSIGHT_CONFIG = INSIGHT_CONFIG;
    })(window);
</script>

<script src="/js/insight.js"></script>

    
</body>
</html>